import _ from 'lodash'
import { isObservableArray, action } from 'mobx'

export function resetDefault (val) {
  return function (target, name, descriptor) {
    if (target.DEFAULT_STATE) {
      target.DEFAULT_STATE[name] = val
    } else {
      target.DEFAULT_STATE = {}
      target.DEFAULT_STATE[name] = val
    }
    return descriptor
  }
}

export default class MobxStore {
  public PARENT: any
  public IS_ROOT: any
  public ROOT: any
  public DEFAULT_STATE: any

  @action
    public ResetDefault (clearAttrArr?: string[]) {
    let self = this
    _.each(this.DEFAULT_STATE, (val: any, key) => {
      let clearFlag = clearAttrArr ? clearAttrArr.indexOf(key) !== -1 : true
            // 触发 Reset 方法
      if (clearFlag) {
        if (_.isFunction(val)) {
                    // @ts-ignore
          self[key] = new val()
        } else {
          self[key] = _.cloneDeep(val)
        }
      }
    })
  }

  static init (instance) {
    let ROOT = instance
        // 标识 root
    ROOT.IS_ROOT = true
    ROOT.STORE_MODULE = {
      root: ROOT
    }
    ROOT.PARENT = ''
    ROOT.ROOT = ROOT
        // 递归遍历
    loopInstance(instance)

    function loopInstance (obj) {
      for (let key in obj) {
        if (key === 'name') {
          let name = obj[key]
          if (!ROOT.STORE_MODULE[name]) {
            ROOT.STORE_MODULE[name] = obj
          } else if (ROOT.STORE_MODULE[name] && !Array.isArray(ROOT.STORE_MODULE[name])) {
            ROOT.STORE_MODULE[name] = [ROOT.STORE_MODULE[name]]
            ROOT.STORE_MODULE[name].push(obj)
          } else if (ROOT.STORE_MODULE[name] && Array.isArray(ROOT.STORE_MODULE[name])) {
            ROOT.STORE_MODULE[name].push(obj)
          }
        }
        if (obj[key] instanceof MobxStore && key !== 'PARENT' && key !== 'ROOT') {
          obj[key].PARENT = obj
          obj[key].ROOT = ROOT
          loopInstance(obj[key])
        }
        if (Array.isArray(obj[key]) || isObservableArray(obj[key])) {
          _.each(obj[key], (item, index) => {
            if (item instanceof MobxStore) {
              obj[key][index].PARENT = obj
              obj[key][index].ROOT = ROOT
              loopInstance(obj[key][index])
            }
          })
        }
      }
    }

    return instance
  }

  getParentStore = <T>(): T => {
    return this.PARENT || undefined
  }

  asyncGetParentStore = <T>(): Promise<T> => {
    return new Promise(resolve => {
      if (this.PARENT) {
        resolve(this.PARENT)
      } else {
        if (!this.IS_ROOT) {
          setTimeout(() => resolve(this.PARENT), 0)
        } else {
          resolve(undefined)
        }
      }
    })
  }

  getRootStore = <T>(): T => {
    return this.ROOT || undefined
  }

  asyncGetRootStore = <T>(): Promise<T> => {
    return new Promise(resolve => {
      if (this.ROOT) {
        resolve(this.ROOT)
      } else {
        setTimeout(() => resolve(this.ROOT),0)
      }
    })
  }

  getModuleByName = <T>(name: string): T => {
    if (this.ROOT) {
      return this.ROOT.STORE_MODULE[name]
    }
  }

  asyncGetModuleByName = <T>(name: string): Promise<T> => {
    return new Promise(resolve => {
      if (this.ROOT) {
        resolve(this.ROOT.STORE_MODULE[name])
      } else {
        setTimeout(() => resolve(this.ROOT.STORE_MODULE[name]),0)
      }
    })
  }

  relink = () => {
    this.ROOT.STORE_MODULE = {}
    this.ROOT = MobxStore.init(this.ROOT)
  }
}
